#include <iostream>
#include <string.h>
#include <cstdio>
#include "SqLiteDbWrapper.h"

using namespace std;

SqLiteDbWrapper::SqLiteDbWrapper(const char* path){ //Constructor tries to open a database connection
                                                     //and complains if it fails
    //sqlite3_extended_result_codes(db, 1);
    if (sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY + SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK){
        cout << "Database Connection Failed: " << sqlite3_errmsg(db);
    }

    //Any operation performed on an unitialised sqlite_statement will cause a runtime error, initialise
    //manually.
    statement = NULL;
}

list<Path> SqLiteDbWrapper::LoadRoute(int routeID){ //Checks Route ID against the database and passes
                                                    //it to private method to retrieve path collection
    char idString[4];
    sprintf(idString,"%i",routeID);
    string sql = "SELECT Name FROM route WHERE RouteID = " + (string)idString;

    sqlite3_prepare_v2(db, sql.c_str(), -1, &statement, NULL);

    if(sqlite3_step(statement) == SQLITE_DONE){
        cout << "Route " << routeID << " is not found! Using empty route\n";
        //TODO: Validation of returned object
        list<Path> nullList;
        return nullList;
    } else {
        cout << "Loading route: " << sqlite3_column_text(statement, 0) << "\n";
        sqlite3_reset(statement);
        return populateRouteData(idString);
    }


}

list<Path> SqLiteDbWrapper::populateRouteData(char* routeIDString){

    //As Route has been successfully validated, we can query all paths
    //that comprise the route and order them
    string sql = "SELECT * FROM path WHERE RouteID = " + (string)routeIDString
                + " ORDER BY \'path\'.\'Order\' ASC";
    sqlite3_prepare_v2(db, sql.c_str(), -1, &statement, NULL);

    //Reusable Path instance for populating vector
    Path path;
    list<Path> routeData;

    while(sqlite3_step(statement) == SQLITE_ROW){
        path.startNode = sqlite3_column_int(statement,1);
        path.endNode = sqlite3_column_int(statement,2);
        path.link = sqlite3_column_int(statement,3);
        path.order = sqlite3_column_int(statement,4);

        routeData.push_back(path);
    }

    //SQLite requires that executed statements be reset to prevent memory errors
    sqlite3_reset(statement);
    sqlite3_finalize(statement);

    return routeData;
}

Link SqLiteDbWrapper::GetLink(int linkID){ //Returns any single link specified by LinkID

    char idString[4];
    sprintf(idString,"%i",linkID);

    string sql = "SELECT * FROM link WHERE LinkID = " + (string)idString;

    sqlite3_prepare_v2(db, sql.c_str(), -1, &statement, NULL);
    sqlite3_step(statement);

    Link link;
    link.linkId = sqlite3_column_int(statement,0);
    link.name = (const char*)sqlite3_column_text(statement,1);
    link.length = sqlite3_column_double(statement,2);
    link.congestion = sqlite3_column_double(statement,3);


    sqlite3_reset(statement);
    sqlite3_finalize(statement);

    return link;
}


Node SqLiteDbWrapper::GetNode(int nodeID){ //Returns any single node specified by NodeID

    char idString[4];
    sprintf(idString,"%i",nodeID);

    string sql = "SELECT * FROM node WHERE NodeID = " + (string)idString;

    sqlite3_prepare_v2(db, sql.c_str(), -1, &statement, NULL);
    sqlite3_step(statement);

    Node node;
    node.nodeId = sqlite3_column_int(statement,0);
    node.latitude = sqlite3_column_double(statement,1);
    node.longitude = sqlite3_column_double(statement,2);
    node.type = (const char *)sqlite3_column_text(statement,3);
    node.congestion = sqlite3_column_double(statement,4);
    node.name = (const char *)sqlite3_column_text(statement,5);

    sqlite3_reset(statement);
    sqlite3_finalize(statement);

    return node;
}

SqLiteDbWrapper::~SqLiteDbWrapper(){//Neatly finish SQLite session once out of scope

    sqlite3_finalize(statement);
    sqlite3_close(db);

}
